
#include <vector>
#include "../crypt/NewRSAKeyPair.h"
#include "../crypt/RSAPublicKey.h"
#include "../crypt/RSAPrivateKey.h"
#include "../crypt/AESSymmKey.h"
#include "NewKeyDB.h"

NewKeyDB::NewKeyDB(const char *filename, const char *WIPpwd, const char *ownname, const char *ownpwd, int persists) : SQLiteDB(filename, persists)
{
   keytablenames = GetTableNamesFromCreateTablesIfNecessary();
   encryptionkeys = new KeyTableView(this, KEY_TABLE_USE_TYPE__ENCRYPTION,
      ENCRYPTION_KEYTABLE_NAME, WIPpwd, ownname, ownpwd);
   decryptionkeys = new KeyTableView(this, KEY_TABLE_USE_TYPE__DECRYPTION, 
      DECRYPTION_KEYTABLE_NAME, WIPpwd, ownname, ownpwd);
   signingkeys = new KeyTableView(this, KEY_TABLE_USE_TYPE__SIGNING, 
      SIGNING_KEYTABLE_NAME, WIPpwd, ownname, ownpwd);
   checkingkeys = new KeyTableView(this, KEY_TABLE_USE_TYPE__CHECKING, 
      CHECKING_KEYTABLE_NAME, WIPpwd, ownname, ownpwd);
   if ((encryptionkeys->GeneratingNewOwnKeysAcceptable()) &&
       (decryptionkeys->GeneratingNewOwnKeysAcceptable()))
   {
      EncryptionKey *enckey = NULL;
      DecryptionKey *deckey = NULL;
      int retval = GenerateNewEncryptionDecryptionKeys(true, ownname, &enckey, &deckey);
      // Insert these keys into the tables next!
      if (retval == 0)
      {
         encryptionkeys->TryToInsertKeyIntoTable(ownname, ownpwd, KEYMARK_CHAINROOT, NULL, enckey);
         decryptionkeys->TryToInsertKeyIntoTable(ownname, ownpwd, KEYMARK_CHAINROOT, NULL, deckey);
         encryptionkeys->RetryGettingOwnKeys(ownpwd);
         decryptionkeys->RetryGettingOwnKeys(ownpwd);
      } else
      {
         errprint("failed to generate new encryption/decryption keys for self '%s'", ownname);
      }
   }
   if ((signingkeys->GeneratingNewOwnKeysAcceptable()) &&
       (checkingkeys->GeneratingNewOwnKeysAcceptable()))
   {
      SigningKey *sgnkey = NULL;
      CheckingKey *chkkey = NULL;
      int retval = GenerateNewSigningCheckingKeys(true, ownname, &sgnkey, &chkkey);
      // Insert these keys into the tables next!
      if (retval == 0)
      {
         signingkeys->TryToInsertKeyIntoTable(ownname, ownpwd, KEYMARK_CHAINROOT, NULL, sgnkey);
         checkingkeys->TryToInsertKeyIntoTable(ownname, ownpwd, KEYMARK_CHAINROOT, NULL, chkkey);
         signingkeys->RetryGettingOwnKeys(ownpwd);
         checkingkeys->RetryGettingOwnKeys(ownpwd);
      } else
      {
         errprint("failed to generate new signing/checking keys for self '%s'", ownname);
      }
   }
}

NewKeyDB::~NewKeyDB()
{
   if (encryptionkeys) delete encryptionkeys;
   encryptionkeys = NULL;
   if (decryptionkeys) delete decryptionkeys;
   decryptionkeys = NULL;
   if (signingkeys) delete signingkeys;
   signingkeys = NULL;
   if (checkingkeys) delete checkingkeys;
   checkingkeys = NULL;
   // doesn't need any destroy string list because all the string are const
   if (keytablenames) delete keytablenames;
   keytablenames = NULL;
}

int NewKeyDB::GenerateEncDecPairIntoDB(int ischained, int isexpendable,
   const char *name, const char *pwd, const char *owner)
{
   // Chained means limited expendability
   int numberofancestors = encryptionkeys->CountKeyChainOfOwner(owner) +
                           decryptionkeys->CountKeyChainOfOwner(owner);
   int ischainroot = (numberofancestors == 0) && ischained;
   EncryptionKey *enckey = NULL;
   DecryptionKey *deckey = NULL;
   int retval = GenerateNewEncryptionDecryptionKeys(ischainroot, name, &enckey, &deckey);
   if (retval == 0)
   {
      char *latest_by_owner_enc = NULL;
      char mark_of_latest_by_owner_enc = '\0';
      char *latest_by_owner_dec = NULL;
      char mark_of_latest_by_owner_dec = '\0';
      if ((ischained) && (!ischainroot))
      {
         // Latest keys in existence must be deprecated when required
         latest_by_owner_enc =
            encryptionkeys->GetLatestKeyNameOfOwner(owner);
         mark_of_latest_by_owner_enc =
            encryptionkeys->GetMarkOfKey(latest_by_owner_enc);
         latest_by_owner_dec =
            decryptionkeys->GetLatestKeyNameOfOwner(owner);
         mark_of_latest_by_owner_dec =
            decryptionkeys->GetMarkOfKey(latest_by_owner_dec);
      }
      char mark_of_this = KEYMARK_PERMANENT;
      mark_of_this = (isexpendable) ? KEYMARK_EXPENDABLE : mark_of_this;
      mark_of_this = (ischainroot) ? KEYMARK_CHAINROOT : mark_of_this;
      mark_of_this = (ischained) ? KEYMARK_CHAINLATEST : mark_of_this;
      sqlite3_mutex_enter(dbmutex);
      if (mark_of_latest_by_owner_enc == KEYMARK_CHAINLATEST)
         encryptionkeys->SetMarkOfKey_unsafe(latest_by_owner_enc, KEYMARK_CHAIN);
      if (mark_of_latest_by_owner_dec == KEYMARK_CHAINLATEST)
         decryptionkeys->SetMarkOfKey_unsafe(latest_by_owner_dec, KEYMARK_CHAIN);
      encryptionkeys->TryToInsertKeyIntoTable_tsafe(name, pwd, mark_of_this, owner, enckey, false);
      decryptionkeys->TryToInsertKeyIntoTable_tsafe(name, pwd, mark_of_this, owner, deckey, false);
      sqlite3_mutex_leave(dbmutex);
      if (latest_by_owner_enc) free(latest_by_owner_enc);
      if (latest_by_owner_dec) free(latest_by_owner_dec);
      return 0;
   } else
   {
      errprint("problem generating encryption/decryption keys");
      return -1;
   }
}

int NewKeyDB::GenerateSgnChkPairIntoDB(int ischained, int isexpendable,
   const char *name, const char *pwd, const char *owner)
{
   // Chained means limited expendability
   int numberofancestors = signingkeys->CountKeyChainOfOwner(owner) +
                           checkingkeys->CountKeyChainOfOwner(owner);
   int ischainroot = (numberofancestors == 0) && ischained;
   SigningKey *sgnkey = NULL;
   CheckingKey *chkkey = NULL;
   int retval = GenerateNewSigningCheckingKeys(ischainroot, name, &sgnkey, &chkkey);
   if (retval == 0)
   {
      char *latest_by_owner_sgn = NULL;
      char mark_of_latest_by_owner_sgn = '\0';
      char *latest_by_owner_chk = NULL;
      char mark_of_latest_by_owner_chk = '\0';
      if ((ischained) && (!ischainroot))
      {
         // Latest keys in existence must be deprecated when required
         latest_by_owner_sgn =
            signingkeys->GetLatestKeyNameOfOwner(owner);
         mark_of_latest_by_owner_sgn =
            signingkeys->GetMarkOfKey(latest_by_owner_sgn);
         latest_by_owner_chk =
            checkingkeys->GetLatestKeyNameOfOwner(owner);
         mark_of_latest_by_owner_chk =
            checkingkeys->GetMarkOfKey(latest_by_owner_chk);
      }
      char mark_of_this = KEYMARK_PERMANENT;
      mark_of_this = (isexpendable) ? KEYMARK_EXPENDABLE : mark_of_this;
      mark_of_this = (ischainroot) ? KEYMARK_CHAINROOT : mark_of_this;
      mark_of_this = (ischained) ? KEYMARK_CHAINLATEST : mark_of_this;
      sqlite3_mutex_enter(dbmutex);
      if (mark_of_latest_by_owner_sgn == KEYMARK_CHAINLATEST)
         signingkeys->SetMarkOfKey_unsafe(latest_by_owner_sgn, KEYMARK_CHAIN);
      if (mark_of_latest_by_owner_chk == KEYMARK_CHAINLATEST)
         checkingkeys->SetMarkOfKey_unsafe(latest_by_owner_chk, KEYMARK_CHAIN);
      signingkeys->TryToInsertKeyIntoTable_tsafe(name, pwd, mark_of_this, owner, sgnkey, false);
      checkingkeys->TryToInsertKeyIntoTable_tsafe(name, pwd, mark_of_this, owner, chkkey, false);
      sqlite3_mutex_leave(dbmutex);
      if (latest_by_owner_sgn) free(latest_by_owner_sgn);
      if (latest_by_owner_chk) free(latest_by_owner_chk);
      return 0;
   } else
   {
      errprint("problem generating signing/checking keys");
      return -1;
   }
}

std::vector<const char *> *NewKeyDB::GetTableNamesFromCreateTablesIfNecessary()
{
   std::vector<const char *> *keytables = new std::vector<const char *>();
   keytables->push_back(ENCRYPTION_KEYTABLE_NAME);
   keytables->push_back(DECRYPTION_KEYTABLE_NAME);
   keytables->push_back(SIGNING_KEYTABLE_NAME);
   keytables->push_back(CHECKING_KEYTABLE_NAME);
   keytables->push_back(NULL);
   EnsureTablesAreCreated(4,
      keytables->at(0), "(keyname char(64) primary key not null, keytype char(32), keydata char(4096), owner char(64), keymark char(1), usedrefcount decimal(10,0), existsrefcount decimal(10, 0), rfc822date char(32))",
      keytables->at(1), "(keyname char(64) primary key not null, keytype char(32), keydata char(4096), owner char(64), keymark char(1), usedrefcount decimal(10,0), existsrefcount decimal(10, 0), rfc822date char(32))",
      keytables->at(2), "(keyname char(64) primary key not null, keytype char(32), keydata char(4096), owner char(64), keymark char(1), usedrefcount decimal(10,0), existsrefcount decimal(10, 0), rfc822date char(32))",
      keytables->at(3), "(keyname char(64) primary key not null, keytype char(32), keydata char(4096), owner char(64), keymark char(1), usedrefcount decimal(10,0), existsrefcount decimal(10, 0), rfc822date char(32))");
   return keytables;
}

int NewKeyDB::GenerateNewEncryptionDecryptionKeys(int ischainroot, const char *name, EncryptionKey **enckey, DecryptionKey **deckey)
{
   int t_generatefunc =
      (ischainroot) ?
         KEY_GENERATE_TYPE_ROOT_ENCRYPT_DECRYPT :
         KEY_GENERATE_TYPE_LEAF_ENCRYPT_DECRYPT;
   switch (t_generatefunc)
   {
      case KEY_ASYMMETRIC_PAIR_RSA:
      {
         NewRSAKeyPair *nrsakp = new NewRSAKeyPair(RSA_BITS, name);
         *enckey = (EncryptionKey *)(nrsakp->pub);
         *deckey = (DecryptionKey *)(nrsakp->prv);
         // RSA key pair does not delete
         delete nrsakp;
         break;
      }
      case KEY_SYMMETRIC_AES:
      {
         AESSymmKey *naessym = new AESSymmKey(AES_BITS, name);
         AESSymmKey *naessym_copy = new AESSymmKey(*naessym);
         *enckey = (EncryptionKey *)(naessym);
         *deckey = (DecryptionKey *)(naessym_copy);
         break;
      }
      default:
      {
         *enckey = NULL;
         *deckey = NULL;
         break;
      }
   }
   if ((*enckey == NULL) || (*deckey == NULL))
   {
      if (*enckey) delete *enckey;
      if (*deckey) delete *deckey;
      return -1;
   }
   return 0;
}

int NewKeyDB::GenerateNewSigningCheckingKeys(int ischainroot, const char *name, SigningKey **sgnkey, CheckingKey **chkkey)
{
   int t_generatefunc =
      (ischainroot) ?
         KEY_GENERATE_TYPE_ROOT_CHECKING_SIGNING :
         KEY_GENERATE_TYPE_LEAF_CHECKING_SIGNING;
   switch (t_generatefunc)
   {
      case KEY_ASYMMETRIC_PAIR_RSA:
      {
         NewRSAKeyPair *nrsakp = new NewRSAKeyPair(RSA_BITS, name);
         *chkkey = (CheckingKey *)(nrsakp->pub);
         *sgnkey = (SigningKey *)(nrsakp->prv);
         // RSA key pair does not delete
         delete nrsakp;
         break;
      }
      default:
      {
         *chkkey = NULL;
         *sgnkey = NULL;
         break;
      }
   }
   if ((*chkkey == NULL) || (*sgnkey == NULL))
   {
      if (*chkkey) delete *chkkey;
      if (*sgnkey) delete *sgnkey;
      return -1;
   }
   return 0;
}
